package coder.taiji.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 功能:
 * 重写了 ThreadFactory 可以设置名字的线程组(不同的业务用不同的线程组)
 * 线程个数固定不能无限增长的。
 * 队列大小固定,队列大小为最大线程个数的10倍。
 * 重写了队列满时候的策略 DiscardPolicy warn日志提示。
 *
 * @author ：helloworldyu
 * @date : 2017/10/19
 */
public class NamedThreadPoolExecutor extends ThreadPoolExecutor {
	private static Logger logger = LoggerFactory.getLogger(NamedThreadPoolExecutor.class);
	private final int maxTaskNum;
	private String poolName;

	public NamedThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
	                               long keepAliveSecond, String poolName) {
		super(corePoolSize, maximumPoolSize, keepAliveSecond, TimeUnit.SECONDS,
				new ArrayBlockingQueue<>(10 * maximumPoolSize),
				new NamedThreadPoolExecutor.NamedThreadFactory(poolName));

		this.poolName = poolName;
		this.maxTaskNum = super.getCorePoolSize() * 10;

		setRejectedExecutionHandler(new DiscardPolicy() {
			@Override
			public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
				logger.error("thread queue is full:{}", getQueue().toString());
			}
		});
	}

	/**
	 * The default concurrent factory
	 */
	static class NamedThreadFactory implements ThreadFactory {
		private static AtomicInteger poolNumber = new AtomicInteger(1);
		private final ThreadGroup group;
		private final AtomicInteger threadNumber = new AtomicInteger(1);
		private final String namePrefix;

		NamedThreadFactory(final String name) {
			SecurityManager s = System.getSecurityManager();
			group = (s != null) ? s.getThreadGroup() :
					Thread.currentThread().getThreadGroup();
			namePrefix = name + "-pool-" +
					poolNumber.getAndIncrement() +
					"-concurrent-";
		}

		@Override
		public Thread newThread(Runnable r) {
			Thread t = new Thread(group, r,
					namePrefix + threadNumber.getAndIncrement(),
					0);
			if (t.isDaemon()) {
				t.setDaemon(false);
			}
			if (t.getPriority() != Thread.NORM_PRIORITY) {
				t.setPriority(Thread.NORM_PRIORITY);
			}
			return t;
		}
	}
}